package com.miku.project.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeCloseModel;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfigKit;
import com.miku.mikuapicommon.model.entity.ProductInfo;
import com.miku.mikuapicommon.model.entity.ProductOrder;
import com.miku.mikuapicommon.model.entity.RechargeActivity;
import com.miku.mikuapicommon.model.entity.User;
import com.miku.project.common.ErrorCode;
import com.miku.project.config.AliPayAccountConfig;
import com.miku.project.config.EmailConfig;
import com.miku.project.exception.BusinessException;
import com.miku.project.mapper.ProductOrderMapper;
import com.miku.project.model.alipay.AliPayAsyncResponse;
import com.miku.project.model.enums.AlipayTradeStatusEnum;
import com.miku.project.model.enums.PaymentStatusEnum;
import com.miku.project.model.vo.PaymentInfoVo;
import com.miku.project.model.vo.ProductOrderVo;
import com.miku.project.model.vo.UserVO;
import com.miku.project.service.*;
import com.miku.project.utils.EmailUtil;
import com.miku.project.utils.RedissonLockUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.miku.project.constant.PayConstant.*;
import static com.miku.project.model.enums.PayTypeStatusEnum.ALIPAY;
import static com.miku.project.model.enums.PaymentStatusEnum.*;

@Service
@Slf4j
@Qualifier("ALIPAY")
public class AliPayOrderServiceImpl extends ServiceImpl<ProductOrderMapper, ProductOrder>implements ProductOrderService {

    @Resource
    public ProductInfoService productInfoService;
    @Resource
    public AliPayAccountConfig aliPayAccountConfig;

    @Resource
    public RedissonLockUtil redissonLockUtil;
    @Resource
    public UserService userService;
    @Resource
    public PaymentInfoService paymentInfoService;
    @Resource
    public RechargeActivityService rechargeActivityService;
    @Resource
    public JavaMailSender mailSender;
    @Resource
    public EmailConfig emailConfig;
    /**
     * 创建订单-支付宝
     * @param productId
     * @param loginUser
     * @param payType
     * @return
     */
    @Override
    public ProductOrderVo getProductOrder(Long productId, UserVO loginUser, String payType) {

        log.info("支付宝支付");
        //查询订单是否被创建
        ProductOrder productOrder = this.getOne(Wrappers.<ProductOrder>lambdaQuery()
                .eq(ProductOrder::getProductId, productId)
                .eq(ProductOrder::getStatus, NOTPAY.getValue())
                .eq(ProductOrder::getPayType, payType)
                .eq(ProductOrder::getUserId, loginUser.getId()));
        if (productOrder == null){
            return null;
        }

        //创建订单
        ProductOrderVo productOrderVo=new ProductOrderVo();
        BeanUtils.copyProperties(productOrder, productOrderVo);
        productOrderVo.setProductInfo(JSONUtil.toBean(productOrder.getProductInfo(), ProductInfo.class));
        productOrderVo.setTotal(productOrder.getTotal().toString());

        return productOrderVo;
    }

    /**
     * 保存产品订单
     * @param productId
     * @param loginUser
     * @return
     */
    @Override
    public ProductOrderVo saveProductOrder(Long productId, UserVO loginUser) {
        ProductInfo productInfo=productInfoService.getById(productId);
        if (productInfo == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"接口不存在");
        }
        //5分钟有效期
        Date date = DateUtil.date(System.currentTimeMillis());
        Date expirationTime = DateUtil.offset(date, DateField.MINUTE, 5);
        String orderNo=ORDER_PREFIX+ RandomUtil.randomNumbers(20);
        ProductOrder productOrder=new ProductOrder();
        productOrder.setUserId(loginUser.getId());
        productOrder.setProductId(productId);
        productOrder.setOrderNo(orderNo);
        productOrder.setOrderName(productInfo.getName());
        productOrder.setTotal(productInfo.getTotal());
        productOrder.setStatus(NOTPAY.getValue());
        productOrder.setPayType(ALIPAY.getValue());
        productOrder.setExpirationTime(expirationTime);
        productOrder.setProductInfo(JSONUtil.toJsonPrettyStr(productInfo));
        productOrder.setAddPoints(productInfo.getAddPoints());

        boolean saveresult=this.save(productOrder);
        if (!saveresult){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"保存订单失败");
        }
        //支付宝创建订单
        AlipayTradePagePayModel model=new AlipayTradePagePayModel();
        model.setOutTradeNo(orderNo);
        model.setSubject(productInfo.getName());
        model.setProductCode("FAST_INSTANT_TRADE_PAY");
        //金额四舍五入
        BigDecimal bigDecimal = new BigDecimal(productOrder.getTotal());
        model.setTotalAmount(String.valueOf(bigDecimal));
        model.setBody(productInfo.getDescription());

        //发起请求
        AlipayTradePagePayRequest request=new AlipayTradePagePayRequest();
        request.setBizModel(model);
        request.setNotifyUrl(aliPayAccountConfig.getNotifyUrl());
        request.setReturnUrl(aliPayAccountConfig.getReturnUrl());

        try {
            AlipayTradePagePayResponse alipayTradePagePayResponse= AliPayApi.pageExecute(request);
            String payUrl = alipayTradePagePayResponse.getBody();
            productOrder.setFormData(payUrl);
        } catch (AlipayApiException e) {
            log.error("支付宝创建订单失败",e);
            throw new RuntimeException(e);
        }
      boolean updateResult=this.updateProductOrder(productOrder);
        if (!updateResult){
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }
        //构建vo
        ProductOrderVo productOrderVo=new ProductOrderVo();
        BeanUtils.copyProperties(productOrder,productOrderVo);
        productOrderVo.setAddPoints(productInfo.getAddPoints().intValue());
        productOrderVo.setCreateTime(productInfo.getCreateTime());
        productOrderVo.setDescription(productInfo.getDescription());
        productOrderVo.setProductType(productInfo.getProductType());
        productOrderVo.setProductInfo(productInfo);
        productOrderVo.setTotal(productInfo.getTotal().toString());
        return productOrderVo;
    }

    /**
     * 更新产品订单
     *
     * @param productOrder
     * @return
     */
    @Override
    public boolean updateProductOrder(ProductOrder productOrder) {
        String formData = productOrder.getFormData();
        Long id = productOrder.getId();
        ProductOrder updateCodeUrl = new ProductOrder();
        updateCodeUrl.setFormData(formData);
        updateCodeUrl.setId(id);
        return this.updateById(updateCodeUrl);
    }

    /**
     * 通过订单号获取产品订单
     *
     * @param orderNo
     * @return
     */
    @Override
    public ProductOrder getProductOrderByOutTradeNo(String orderNo) {
        ProductOrder productOrder = this.getOne(Wrappers.<ProductOrder>lambdaQuery().eq(ProductOrder::getOrderNo, orderNo));
        if (productOrder == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"订单不存在");
        }
        return productOrder;
    }

    /**
     * 根据订单号修改订单状态
     *
     * @param orderNo
     * @param orderStatus
     * @return
     */
    @Override
    public boolean updateOrderStatusByOrderNo(String orderNo, String orderStatus) {
        boolean update = this.update(Wrappers.<ProductOrder>lambdaUpdate()
                .eq(ProductOrder::getOrderNo, orderNo)
                .set(ProductOrder::getStatus, orderStatus));

        return update;
    }

    /**
     * 付款通知
     * 处理付款通知
     *
     * @param notifyDate
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String doPaymentNotify(String notifyDate, HttpServletRequest request) {
        Map<String,String> params =AliPayApi.toMap(request);
        AliPayAsyncResponse aliPayAsyncResponse = JSONUtil.toBean(JSONUtil.toJsonStr(params), AliPayAsyncResponse.class);
        String redissonLock="notify:AlipayOrder:lock"+aliPayAsyncResponse.getOutTradeNo();
        return redissonLockUtil.redissonDistributedLocks(redissonLock,()->{
            String result;
            try {
                result=chackAlipayOrder(aliPayAsyncResponse,params);
            } catch (AlipayApiException e) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,"支付宝回调验签失败");
            }
            if (!"success".equals(result)){
                return result;
            }
            String doAliPayOrderBusinessResult=this.doAliPayOrderBusiness(aliPayAsyncResponse);
            if (StringUtils.isBlank(doAliPayOrderBusinessResult)){
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,"支付宝回调业务处理失败");
            }
            return doAliPayOrderBusinessResult;
        });
    }

    /**
     * 检查订单状态(微信查单接口)
     * 删除超时订单
     * @param productOrder
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processingTimedOutOrders(ProductOrder productOrder) {
        String orderNo = productOrder.getOrderNo();

        try {
            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
            model.setOutTradeNo(orderNo);
            AlipayTradeQueryResponse response = AliPayApi.tradeQueryToResponse(model);
            //本地创建了订单,但用户没有扫码,支付宝端没有订单
            if (!response.getCode().equals(RESPONSE_CODE_SUCCESS)){
                //更新本地订单状态
                this.updateOrderStatusByOrderNo(orderNo, PaymentStatusEnum.CLOSED.getValue());
                log.info("订单号:{}超时未支付,已关闭",orderNo);
                return;
            }
            String tradeStatus = AlipayTradeStatusEnum.findByName(response.getTradeStatus()).getPaymentStatusEnum().getValue();
            //订单没有支付就关闭订单,更新本地订单状态
            if (tradeStatus.equals(NOTPAY.getValue())|| tradeStatus.equals(CLOSED.getValue())){
                closedOrderByOrderNo(orderNo);
                this.updateOrderStatusByOrderNo(orderNo, PaymentStatusEnum.CLOSED.getValue());
                log.info("订单号:{}超时未支付,已关闭",orderNo);
                return;
            }
            if (tradeStatus.equals(SUCCESS.getValue())){
                //订单已支付更新商户端的订单状态
                boolean updateStatus = this.updateOrderStatusByOrderNo(orderNo, SUCCESS.getValue());
                //补发积分到用户钱包
                boolean addWalletBalance = userService.addWalletBalance(productOrder.getUserId(), productOrder.getAddPoints().intValue());
                //保存支付记录
                PaymentInfoVo paymentInfoVo=new PaymentInfoVo();
                paymentInfoVo.setAppid(response.getOpenId());
                paymentInfoVo.setOutTradeNo(response.getOutTradeNo());
                paymentInfoVo.setTransactionId(response.getTradeNo());
                paymentInfoVo.setTradeType("电脑网站支付");
                paymentInfoVo.setTradeState(response.getTradeStatus());
                paymentInfoVo.setTradeStateDesc("支付成功");
                paymentInfoVo.setSuccessTime(String.valueOf(response.getSendPayDate()));
                WxPayOrderQueryV3Result.Payer payer = new WxPayOrderQueryV3Result.Payer();
                payer.setOpenid(response.getBuyerOpenId());
                paymentInfoVo.setPayer(payer);
                WxPayOrderQueryV3Result.Amount amount = new WxPayOrderQueryV3Result.Amount();
                amount.setTotal(new BigDecimal(response.getTotalAmount()).multiply(new BigDecimal("100")).intValue());
                amount.setPayerTotal(new BigDecimal(response.getReceiptAmount()).multiply(new BigDecimal("100")).intValue());
                amount.setCurrency(response.getPayCurrency());
                amount.setPayerCurrency(response.getPayCurrency());
                paymentInfoVo.setAmount(amount);
                boolean paymentInfo = paymentInfoService.createPaymentInfo(paymentInfoVo);
                if (updateStatus && addWalletBalance && paymentInfo){
                    throw new BusinessException(ErrorCode.OPERATION_ERROR,"支付成功,订单状态更新失败");
                }
                //更新活动表
                saveRechargeActivity(productOrder);
                sendSuccessEmail(productOrder,response.getTotalAmount());
                log.info("订单号:{}支付成功",orderNo);
            }

        } catch (AlipayApiException e) {
            log.error("订单号:{}支付失败",orderNo);
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"支付失败");
        }

    }

    /**
     * 检查订单状态(支付宝查单接口)
     * @param orderNo
     */
    public void closedOrderByOrderNo(String orderNo) {
        AlipayTradeCloseModel closeModel = new AlipayTradeCloseModel();
        closeModel.setOutTradeNo(orderNo);
        AlipayTradeCloseRequest closeRequest = new AlipayTradeCloseRequest();
        closeRequest.setBizModel(closeModel);
        try {
            AliPayApi.doExecute(closeRequest);
        } catch (AlipayApiException e) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"订单关闭失败");
        }
    }

    /**
     * 处理支付宝回调
     * @param response
     * @return
     */
    @SneakyThrows
    public String doAliPayOrderBusiness(AliPayAsyncResponse response) {
        String outTradeNo = response.getOutTradeNo();
        ProductOrder productOrder = this.getProductOrderByOutTradeNo(outTradeNo);
        //处理重复通知
        if (SUCCESS.getValue().equals(productOrder.getStatus())){
            return "success";
        }
        //业务代码
        //更新订单状态
        boolean a = this.updateOrderStatusByOrderNo(outTradeNo, SUCCESS.getValue());
        //更新用户积分
        boolean b = userService.addWalletBalance(productOrder.getUserId(), productOrder.getAddPoints().intValue());
        //保存支付记录
        PaymentInfoVo paymentInfoVo=new PaymentInfoVo();
        paymentInfoVo.setAppid(response.getAppId());
        paymentInfoVo.setOutTradeNo(response.getOutTradeNo());
        paymentInfoVo.setTransactionId(response.getTradeNo());
        paymentInfoVo.setTradeType("电脑支付");
        paymentInfoVo.setTradeState(response.getTradeStatus());
        paymentInfoVo.setTradeStateDesc("支付成功");
        paymentInfoVo.setSuccessTime(response.getNotifyTime());
        WxPayOrderQueryV3Result.Payer payer= new WxPayOrderQueryV3Result().getPayer();
        payer.setOpenid(response.getBuyerId());
        paymentInfoVo.setPayer(payer);
        WxPayOrderQueryV3Result.Amount amount= new WxPayOrderQueryV3Result().getAmount();
        amount.setTotal(new BigDecimal(response.getTotalAmount()).multiply(new BigDecimal("100")).intValue());
        amount.setPayerTotal(new BigDecimal(response.getReceiptAmount()).multiply(new BigDecimal("100")).intValue());
        amount.setCurrency("CNY");
        amount.setPayerCurrency("CNY");
        paymentInfoVo.setAmount(amount);
        boolean paymentResult=paymentInfoService.createPaymentInfo(paymentInfoVo);
        //更新活动表
        boolean rechargeActivity=saveRechargeActivity(productOrder);
        if (a&&b&&paymentResult&&rechargeActivity){
            log.info("【支付宝支付】订单处理成功");
            //发送邮件
            sendSuccessEmail(productOrder,response.getTotalAmount());
            return "success";
        }
        throw new BusinessException(ErrorCode.OPERATION_ERROR,"支付宝支付失败");
    }

    /**
     * 发送邮件
     * @param productOrder
     * @param totalAmount
     */
    public void sendSuccessEmail(ProductOrder productOrder, String totalAmount) {
        User user = userService.getById(productOrder.getUserId());
        if (StringUtils.isNotBlank(user.getEmail())){
            ProductOrder outTradeNo = this.getProductOrderByOutTradeNo(productOrder.getOrderNo());
            try {
                new EmailUtil().sendPaySuccessEmail(user.getEmail(),mailSender,emailConfig,outTradeNo.getOrderName(),String.valueOf(totalAmount));
                log.info("发送邮件:{}，成功",user.getEmail());
            } catch (Exception e) {
                log.error("发送邮件:{}，失败",user.getEmail());
            }


        }
    }

    /**
     * 保存充值活动
     * @param productOrder
     * @return
     */
    public boolean saveRechargeActivity(ProductOrder productOrder) {
        boolean update = rechargeActivityService.update(Wrappers.<RechargeActivity>lambdaUpdate()
                .set(RechargeActivity::getUserId, productOrder.getUserId())
                .set(RechargeActivity::getProductId, productOrder.getProductId())
                .set(RechargeActivity::getOrderNo, productOrder.getOrderNo()));
        if (!update){
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"保存充值活动失败");
        }
        return true;
    }

    /**
     * 检查alipay里面的参数
     * @param response
     * @param params
     * @return
     */
    public String chackAlipayOrder(AliPayAsyncResponse response, Map<String, String> params) throws AlipayApiException {
        String result="failure";
        boolean verifyResult = AlipaySignature.rsaCertCheckV1(params, AliPayApiConfigKit.getAliPayApiConfig().getAliPayPublicKey(),
                AliPayApiConfigKit.getAliPayApiConfig().getCharset(),
                AliPayApiConfigKit.getAliPayApiConfig().getSignType());
        if (!verifyResult){
            return result;
        }
        // 1.验证该通知数据中的 out_trade_no 是否为商家系统中创建的订单号。
        ProductOrder productOrder = this.getProductOrderByOutTradeNo(response.getOutTradeNo());
        if (productOrder==null){
            log.error("订单不存在");
            return result;
        }
        // 2.判断 total_amount 是否确实为该订单的实际金额（即商家订单创建时的金额）。
        int totalAmount = new BigDecimal(response.getTotalAmount()).multiply(new BigDecimal("100")).intValue();
        if (totalAmount!=productOrder.getTotal()){
            log.error("金额不一致");
            return result;
        }
        // 3.校验通知中的 seller_id（或者 seller_email) 是否为 out_trade_no 这笔单据的对应的操作方（有的时候，一个商家可能有多个 seller_id/seller_email）。
        String sellerId = aliPayAccountConfig.getSellerId();
        if (!response.getSellerId().equals(sellerId)){
            log.error("卖家账号校验失败");
            return result;
        }
        //4.验证app_id
        String appId = aliPayAccountConfig.getAppId();
        if (!response.getAppId().equals(appId)){
            log.error("appId校验失败");
            return result;
        }
        // 状态 TRADE_SUCCESS 的通知触发条件是商家开通的产品支持退款功能的前提下，买家付款成功。
        String tradeStatus = response.getTradeStatus();
        if (!tradeStatus.equals(TRADE_SUCCESS)){
            log.error("交易失败");
            return result;
        }
        return "success";
    }
}
